package baiqitun.stupro.security.service.impl;

import baiqitun.stupro.common.constant.SysConstant;
import baiqitun.stupro.common.enums.BooleanEnum;
import baiqitun.stupro.common.exception.enums.support.DisabledExceptionEnum;
import baiqitun.stupro.common.exception.enums.support.StuproExceptionEnum;
import baiqitun.stupro.common.exception.enums.support.ExistedExceptionEnum;
import baiqitun.stupro.common.exception.enums.support.NotFoundExceptionEnum;
import baiqitun.stupro.common.exception.support.DisabledException;
import baiqitun.stupro.common.exception.support.NotFoundException;
import baiqitun.stupro.common.model.TreeModel;
import baiqitun.stupro.common.service.EntityEnabledChecker;
import baiqitun.stupro.common.service.TreeNodeService;
import baiqitun.stupro.common.service.support.MyServiceImpl;
import baiqitun.stupro.common.service.support.TreeNodeServiceImpl;
import baiqitun.stupro.security.entity.SysPermission;
import baiqitun.stupro.security.entity.SysRole;
import baiqitun.stupro.security.enums.SysRoleEnum;
import baiqitun.stupro.security.mapper.SysPermissionMapper;
import baiqitun.stupro.security.model.vo.Ui;
import baiqitun.stupro.security.service.SysPermissionService;
import baiqitun.stupro.security.service.SysRoleService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import baiqitun.stupro.common.exception.support.ExistedException;
import baiqitun.stupro.common.exception.StuproException;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotBlank;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 许可表 服务实现类
 * </p>
 *
 * @author kedaji
 * @since 2020-03-25
 */
@Service
public class SysPermissionServiceImpl extends TreeNodeServiceImpl<SysPermissionMapper, SysPermission> implements SysPermissionService, EntityEnabledChecker<SysPermission> {
    @Autowired
    private SysRoleService sysRoleService;

    @CachePut("permission")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean save(@Validated SysPermission permission) {
        checkPermissionForSave(permission);
        return super.save(permission);
    }

    @CachePut("permission")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveRoot(SysPermission permission) {
        checkPermissionForSave(permission);
        this.save(permission);
    }

    @CachePut("permission")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveChild(SysPermission permission) {
        checkDisabled(permission.getParentId(), new DisabledException(DisabledExceptionEnum.PERMISSION_DISABLED));
        // 在保存子许可之前先把父许可的isLeaf属性改为0并保存修改人用户名
        SysPermission parentPermission = getById(permission.getParentId());
        parentPermission.setIsLeaf(BooleanEnum.FALSE.getValue());
        updateById(parentPermission);
        this.save(permission);
    }

    /**
     * 获取用户角色持有的子许可
     * @param id 节点id
     * @param queryEntity 查询条件实体
     * @param conditions 过滤条件
     * @return 许可列表
     */
    @Override
    protected List<SysPermission> getChildren(String id, SysPermission queryEntity, Map<String, Object> conditions){
        List<SysPermission> permissions = list(new QueryWrapper<>(queryEntity).eq("parent_id", id));
        List<SysRole> roles = (List<SysRole>) conditions.get("roles");
        List<SysPermission> finalPermission = new LinkedList<>();
        // 将查询的到的子许可根据用户角色进行过滤
        for (SysPermission tmpPermission: permissions){
            for (SysRole tmpRole: roles){
                boolean hasPermission = hasRelation(tmpRole.getId(), tmpPermission.getId());
                if (hasPermission){
                    finalPermission.add(tmpPermission);
                    break;
                }
            }
        }
        return finalPermission;
    }

    @Override
    public List<TreeModel<SysPermission>> listRootTree(SysPermission queryEntity, Map<String, Object> conditions) {
        LinkedList<TreeModel<SysPermission>> trees = new LinkedList<>();
        List<SysRole> roles = (List<SysRole>) conditions.get("roles");
        for (SysRole tmpRole: roles){
            List<SysPermission> permissions = listRootPermission(tmpRole.getId(), null);
            for (SysPermission tmpPermission: permissions){
                TreeModel<SysPermission> tree = getTree(tmpPermission.getId(), new SysPermission(), conditions);
                trees.add(tree);
            }
        }
        return trees;
    }

    private List<SysPermission> listRootPermission(String roleId, String group) {
        SysPermission queryPermission = new SysPermission();
        queryPermission.setRoleId(roleId).setGroup(group);
        return list(new QueryWrapper<>(queryPermission).isNull("parent_id").eq("enabled", "1"));
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<SysPermission> listByRoleId(String roleId) {
        SysPermission queryPermission = new SysPermission();
        queryPermission.setRoleId(roleId);
        return list(new QueryWrapper<>(queryPermission).eq("enabled", "1"));
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<SysPermission> listRootPermissionByRoleCode(String roleCode, String group) {
        SysRole role = sysRoleService.getRoleByCode(roleCode);
        return listRootPermission(role.getId(), group);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean removeById(String id, @NotBlank String updateBy) {
        checkDisabled(id, new DisabledException(DisabledExceptionEnum.PERMISSION_DISABLED));
        // 检查当前许可是否是顶级许可
        SysPermission existedPermission = getEnabledEntity(id, new DisabledException(DisabledExceptionEnum.PERMISSION_DISABLED));
        if (StringUtils.isNotBlank(existedPermission.getParentId())){
            // 不是顶级许可
            System.out.println("不是顶级许可");
            // 检查父许可是否还有其他子许可，如果没有，将父许可的isLeaf置为1
            String parentId = existedPermission.getParentId();
            checkDisabled(parentId,  new DisabledException(DisabledExceptionEnum.PERMISSION_DISABLED));
            SysPermission queryPermission = new SysPermission();
            queryPermission.setParentId(parentId);
            int count = count(new QueryWrapper<>(queryPermission));
            if (count == 1){
                SysPermission parentPermission = new SysPermission();
                parentPermission.setIsLeaf(BooleanEnum.TRUE.getValue()).setId(parentId).setUpdateBy(updateBy);
                updateById(parentPermission);
            }
        } else {
            SysPermission permission = new SysPermission();
            permission.setId(id).setUpdateBy(updateBy);
            updateById(permission);
        }
        return removeById(id);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysPermission getPermission(String uri, String method) {
        HttpMethod.valueOf(method);
        SysPermission queryPermission = new SysPermission();
        queryPermission.setUri(uri).setMethod(method);
        return getOne(new QueryWrapper<>(queryPermission));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void relevance(String roleId, String permissionId, String createBy) {
        sysRoleService.checkNotFound(roleId, new NotFoundException(NotFoundExceptionEnum.ROLE_NOT_FOUND));
        checkNotFound(permissionId, new NotFoundException(NotFoundExceptionEnum.PERMISSION_NOT_FOUND));
        SysPermission permission = new SysPermission();
        permission.setRoleId(roleId).setId(permissionId).setCreateBy(createBy);
        updateById(permission);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void relevanceByRoleCode(String roleCode, String permissionId, String createBy) {
        SysRole role = sysRoleService.getRoleByCode(roleCode);
        if (role == null){
            throw new NotFoundException(NotFoundExceptionEnum.ROLE_NOT_FOUND);
        }
        relevance(role.getId(), permissionId, createBy);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public boolean hasRelation(String roleId, String permissionId) {
        SysPermission permission = getExistedEntity(permissionId, new NotFoundException(NotFoundExceptionEnum.PERMISSION_NOT_FOUND));
        if (permission.getRoleId() == null){
            return false;
        }
        return permission.getRoleId().equals(roleId);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public boolean hasRelationByRoleCode(String roleCode, String permissionId) {
        SysRole role = sysRoleService.getRoleByCode(roleCode);
        return hasRelation(role.getId(), permissionId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateById(@Validated SysPermission permission) {
        checkDisabled(permission.getId(), new DisabledException(DisabledExceptionEnum.PERMISSION_DISABLED));
        checkPermissionForUpdate(permission);
        return super.updateById(permission);
    }


    private void checkPermissionForUpdate(SysPermission permission){
        // TODO 检查许可更新参数是否合法
    }

    private void checkPermissionForSave(SysPermission permission){
        String parentId = permission.getParentId();
        String group = permission.getGroup();
        String uri = permission.getUri();
        String method = permission.getMethod();
        String isUi = permission.getIsUi();

        if (StringUtils.isBlank(isUi)){
            throw new StuproException(StuproExceptionEnum.PARAMETER_CANNOT_NULL)
                    .put(SysPermission.class,"isUi", isUi);
        }

        switch (isUi){
            case "1":
                // 只有顶级UI许可才可以分组
                if (StringUtils.isBlank(parentId) && StringUtils.isNotBlank(group)){
                    throw new StuproException(StuproExceptionEnum.ILLEGAL_PARAMETER)
                            .put(SysPermission.class.getSimpleName()+"isUi", isUi)
                            .put(SysPermission.class.getSimpleName()+"group", group);
                }
                break;
            case "0":
                // 非UI许可不能分组(group)或带有图标(icon)
                if (StringUtils.isNotBlank(group) || StringUtils.isNotBlank(permission.getIcon())){
                    throw new StuproException(StuproExceptionEnum.ILLEGAL_PARAMETER)
                            .put(SysPermission.class.getSimpleName()+"isUi", isUi)
                            .put(SysPermission.class.getSimpleName()+"group", group)
                            .put(SysPermission.class.getSimpleName()+"icon", permission.getIcon());
                }

                // 非UI许可的URI与Method是必填的
                if (StringUtils.isBlank(uri) || StringUtils.isBlank(method)){
                    throw new StuproException(StuproExceptionEnum.PARAMETER_CANNOT_NULL)
                            .put(SysPermission.class,"isUi",isUi)
                            .put(SysPermission.class, "path",uri)
                            .put(SysPermission.class, "method", method);
                }

                // 检查请求方式是否合法
                if (StringUtils.isNotBlank(method)){
                    try {
                        HttpMethod.valueOf(method.toUpperCase());
                    } catch (IllegalArgumentException e){
                        throw new StuproException(StuproExceptionEnum.ILLEGAL_PARAMETER)
                                .put(SysPermission.class,"method", method);
                    }
                }
                break;
            default:
                throw new StuproException(StuproExceptionEnum.ILLEGAL_PARAMETER)
                        .put(SysPermission.class,"isUi", isUi);

        }
    }

    @Override
    public void checkDisabled(String id, DisabledException e) {
        SysPermission permission = getExistedEntity(id, new NotFoundException(NotFoundExceptionEnum.PERMISSION_NOT_FOUND));
        if (BooleanEnum.FALSE.getValue().equals(permission.getEnabled())){
            throw new DisabledException(DisabledExceptionEnum.USER_DISABLED);
        }
    }

    @Override
    public SysPermission getEnabledEntity(String id, DisabledException e) {
        SysPermission permission = getExistedEntity(id,  new NotFoundException(NotFoundExceptionEnum.PERMISSION_NOT_FOUND));
        if (BooleanEnum.FALSE.getValue().equals(permission.getEnabled())){
            throw new DisabledException(DisabledExceptionEnum.USER_DISABLED);
        }
        return permission;
    }
}
